﻿using System.IO;
using System.Reflection;

namespace LS.Screening.UI.Common
{
    public class ClientUpdator
    {
        static List<string> clientRefrenceNames = new List<string>();

        static ClientUpdator()
        {
            var exceptNames = AppDomain.CurrentDomain.GetAssemblies().Select(p => p.GetName().Name).ToList();
            clientRefrenceNames.AddRange(exceptNames.ToArray());
        }

        public static Action<string> MessageAction { get; set; }

        public static Action<float> FileDownLoadPercentAction { get; set; }

        #region 服务端调用

        /// <summary>
        /// 服务端读取某个文件的内容
        /// </summary>
        /// <param name="file">文件相对路径及名称</param>
        /// <param name="directory">文件所在目录</param>
        /// <param name="startPoint">部分读取时，文件流起点位置</param>
        /// <param name="length">部分读取时，要读取的数据大小</param>
        /// <returns></returns>
        private static async Task<byte[]> ReadFile(string file, bool isSetupFile = false, int startPoint = -1, int length = -1)
        {
            return await ClientHelper.ReadFile(file, isSetupFile, startPoint, length);
        }


        /// <summary>
        /// 获取安装目录中的文件信息，仅限于客户端调用
        /// </summary>
        /// <param name="searchPattern"></param>
        /// <returns></returns>
        private static async Task<FileDescription> GetSetupFile(string searchPattern = "Setup*")
        {
            return await ClientHelper.GetSetupFile(searchPattern);
        }

        #endregion

        #region 客户端

        /// <summary>
        /// 获取更新目录中的文件，客户端调用时获取客户端已有文件的信息，用于与服务端的文件进行比较
        /// </summary>
        /// <param name="lastUpdateTime">最后更新时间</param>
        /// <returns></returns>
        private static List<FileDescription> GetUpdateFiles(DateTime? lastUpdateTime = null)
        {
            var path = Directory.GetCurrentDirectory();
            var list = new List<FileDescription>();
            var files = Directory.GetFiles(path, "*", SearchOption.AllDirectories);
            foreach (var file in files)
            {
                var fi = new FileInfo(file);
                if (lastUpdateTime != null)
                {
                    if (fi.LastWriteTimeUtc < lastUpdateTime)
                    {
                        continue;
                    }
                }
                var fileDesecription = new FileDescription()
                {
                    FilePath = file.Replace(path, ""),
                    Name = fi.Name,
                    UpdateTime = fi.LastWriteTimeUtc,
                    FileSize = (int)fi.Length
                };
                list.Add(fileDesecription);
            }
            return list.OrderBy(p => p.UpdateTime).ToList();
        }

        /// <summary>
        /// 更新(每次比较全部文件的时间)
        /// </summary>
        public static async Task Update()
        {
            MessageAction?.Invoke("正在检查文件...");
            var remotingFiles = await ClientHelper.GetUpdateFiles(null);
            if (remotingFiles.Count > 0)
            {
                MessageAction?.Invoke("正在更新文件...");
                var needUpdateFiles = new List<FileDescription>();
                var localFiles = GetUpdateFiles();
                //与本地文件进行比较
                for (int i = 0; i < remotingFiles.Count; i++)
                {
                    var remotingFile = remotingFiles[i];
                    var name = remotingFile.Name.Substring(0, remotingFile.Name.LastIndexOf("."));
                    if (clientRefrenceNames.Any(p => p == name))
                    {
                        continue;
                    }
                    var remotingFilePath = remotingFile.FilePath.Replace("/", "\\");
                    var localFile = localFiles.FirstOrDefault(p => p.FilePath.Replace("/", "\\").Equals(remotingFilePath, StringComparison.OrdinalIgnoreCase) && p.Name.Equals(remotingFile.Name, StringComparison.OrdinalIgnoreCase));
                    if (localFile == null)//客户端不存在的文件加入更新队列
                    {
                        needUpdateFiles.Add(remotingFile);
                    }
                    else if (remotingFile.UpdateTime > localFile.UpdateTime.AddSeconds(10))//客户端的时间较旧的加入更新队列
                    {
                        needUpdateFiles.Add(remotingFile);
                    }
                }
                if (needUpdateFiles.Count != 0)//如果有文件需要更新
                {
                    MessageAction?.Invoke("发现" + needUpdateFiles.Count + "个文件需更新！");
                    foreach (var fd in needUpdateFiles)
                    {
                        MessageAction?.Invoke("更新:" + fd.Name);
                        try
                        {
                            int c = 5;
                            while (c > 0)
                            {
                                var filePath = await ReadFileFromServer(fd);
                                var f = new FileInfo(filePath);
                                if (f.Length > 0)
                                {
                                    break;
                                }
                                c--;
                            }
                        }
                        catch
                        {
                            continue;
                        }
                    }
                    MessageAction?.Invoke("更新完成:" + needUpdateFiles.Count + "个文件被更新！");
                }
            }
        }

        /// <summary>
        /// 更新包检查
        /// </summary>
        /// <param name="searchPattern">搜索匹配字符，如Setup*,*.msi,默认为Setup*</param>
        /// <param name="searchFile">进行安装包比较的文件</param>
        /// <returns></returns>
        public static async Task<string> CheckSetup(string searchPattern = null)
        {
            MessageAction?.Invoke("正在检查新的安装包...");
            var fd = await ClientHelper.GetSetupFile(searchPattern ?? string.Empty);
            if (fd == null) return string.Empty;

            var version = Assembly.GetEntryAssembly().GetName().Version.ToString();
            var newVersion = fd.Name.Substring(fd.Name.LastIndexOf("_") + 1);
            newVersion = newVersion.Substring(0, newVersion.LastIndexOf('.'));
            if (IsNewVersion(version, newVersion))
            {
                MessageAction?.Invoke("检查到新的安装包，正在下载...");
                return await ReadFileFromServer(fd, true);
            }
            return string.Empty;
        }

        /// <summary>
        /// 客户端读取文件并写入本地
        /// </summary>
        /// <param name="fd"></param>
        /// <param name="isSetupFile">是否为安装包文件，是则下载到Temp目录中，否则下载到本地目录中</param>
        /// <returns></returns>
        private static async Task<string> ReadFileFromServer(FileDescription fd, bool isSetupFile = false)
        {
            var filePath = isSetupFile ? Path.Combine(Path.GetTempPath(), AppDomain.CurrentDomain.FriendlyName) : Directory.GetCurrentDirectory();
            filePath = Path.Combine(filePath, fd.FilePath);
            var dir = Path.GetDirectoryName(filePath);
            if (!Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }

            int startPosition = 0, length = 1024 * 1024;

            filePath = filePath.Replace("/", "\\");
            var file = new FileInfo(filePath);
            if (File.Exists(filePath))
            {
                if (isSetupFile)
                {
                    startPosition = (int)file.Length;
                }
                else
                {
                    file.Delete();
                }
            }
            if (fd.FileSize > length)
            {
                FileDownLoadPercentAction?.Invoke(0);
                using (var fs = file.OpenWrite())
                {
                    while (true)
                    {
                        await Task.Delay(10);
                        var data = await ClientHelper.ReadFile(fd.FilePath, isSetupFile, startPosition, length);
                        fs.Seek(startPosition, SeekOrigin.Begin);
                        if (data.Length > 0)
                        {
                            await fs.WriteAsync(data, 0, data.Length);
                            await fs.FlushAsync();
                        }
                        if (data.Length < length)
                        {
                            FileDownLoadPercentAction?.Invoke(100);
                            break;
                        }
                        startPosition += data.Length;
                        FileDownLoadPercentAction?.Invoke(startPosition * 100.0f / fd.FileSize);
                    }
                    fs.Close();
                    return filePath;
                }
            }
            else
            {
                await Task.Delay(10);
                var temp = await ClientHelper.ReadFile(fd.FilePath, isSetupFile, -1, -1);
                if (temp != null && temp.Length > 0)
                {
                    await File.WriteAllBytesAsync(filePath, temp);
                }
                return filePath;
            }
        }

        /// <summary>
        /// 版本比较
        /// </summary>
        /// <param name="localVersion"></param>
        /// <param name="serverVersion"></param>
        /// <returns></returns>
        static bool IsNewVersion(string localVersion, string serverVersion)
        {
            var lr = localVersion.Split(".").Select(p => int.Parse(p)).ToList();
            var sr = serverVersion.Split(".").Select(p => int.Parse(p)).ToList();
            if (sr.Count != lr.Count) return false;
            for (int i = 0; i < lr.Count; i++)
            {
                if (lr[i] < sr[i]) return true;
            }
            return false;
        }

        #endregion
    }
}
